<?php
/**
 * Created by PhpStorm.
 * User: 牛赞赞
 * Date: 2021/1/20
 * Time: 16:14
 */

namespace App\Game\Logic;

use App\Game\Conf\MainCmd;
use App\Game\Conf\SubCmd;
use App\Game\Core\Packet;
use App\Game\Core\AStrategy;
use App\Task\CallNextTask;
use App\Task\GameSyncTask;
use App\Task\OutCardTimerTask;

class Caller extends AStrategy
{
    public function exec()
    {
        $account    = $this->_params['userinfo']['account'];
        $call_catch = $this->_params['data']['type'];
        $val        = $this->_params['data']['val'];
        $game_conf  = $this->game_conf;
        $user_room_map_key = sprintf($game_conf['user_room'],$account);
        $room_no = redis()->get($user_room_map_key);
        $room_data_key  = sprintf($game_conf['user_room_data'],$room_no);
        $room_data      = redis()->hGetAll($room_data_key);
        $user_room_data = json_decode($room_data[$account],true);
        $times = $room_data['call_times']+1;
        $call_timer = $this->getCallerTimes($room_no);


        // 叫牌顺序
        $call_arr = json_decode($room_data['call_order'],true);

        // 获取房间所有fds
        $fds = $this->getRoomFds($account);

        // 下一家座位号
        $next_account  = $this->findNextAccount($call_arr,$account);
        $next_chair_id = $this->findChairId($room_no,$next_account);

        // 情况计时器数据
        $this->cleanCallLoop($room_no);

        // 这里只调叫牌通知
        if(!isset($room_data['master']) && ($room_data['call_times'] <= 4)){
            $next_times     = $times+1;
            $call_timer_num = $call_timer+1;

            // 更新叫牌次数
            $this->updateRoomData($room_no,['call_times'=>$times]);

            // 判断是否抢地主翻倍
            if(($call_catch == 2) && ($val == 1)){
                $this->doubleRate($room_no);
            }

            // 更新叫牌信息
            $params= ['call_catch'=>$call_catch,'val'=>$val];
            $this->setRoomUserData($room_no,$account,$params);

            // 广播叫地主消息
            $rate = $this->getUserRate($room_no,$account);
            $this->broadcastRoom($fds,$user_room_data['chair_id'],$rate,$call_catch,$val);

            if($times >= 3){
                //是否产生地主，摸底牌操作
                $this->catchGameCardResp($fds,$room_no);
            }else{
                //投递通知下家
                if(($call_catch == 1) && ($val == 1)){
                    $call_catch = 2;
                }elseif(($call_catch == 2) && ($val == 1)){
                    $call_catch = 2;
                }elseif(($call_catch == 1) && ($val == 0)){
                    $call_catch = 1;
                }else{
                    $call_catch = 2;
                }
                $task = container()->get(CallNextTask::class);
                $task->callNext($room_no,$next_chair_id,$call_catch,$next_times,$call_timer_num);
            }
        }
    }

    /**
     * 清空定时器数据
     * @param $room_no
     */
    protected function cleanCallLoop($room_no){
        $game_conf = $this->game_conf;
        $call_loop_key = sprintf($game_conf['user_room_call'],$room_no);
        redis()->del($call_loop_key);
    }

    /**
     * @param $room_no
     */
    protected function cleanCallTimes($room_no){
        $room_loop_key = sprintf($this->game_conf['user_room_callTimes'],$room_no);
        redis()->del($room_loop_key);
    }

    /**
     * 增加叫牌次数
     * @param $room_no
     * @param $params
     */
    protected function updateRoomData($room_no,$params){
        $game_conf = config('game');
        $room_data_key = sprintf($game_conf['user_room_data'],$room_no);
        foreach ($params as $key=>$val){
            redis()->hSet($room_data_key, (string)$key, $val);
        }
    }

    /**计时器序号
     * @param $room_no
     * @return int
     */
    protected function getCallerTimes($room_no){
        $game_conf = config('game');
        $user_room_callTimes = sprintf($game_conf['user_room_callTimes'],$room_no);
        if(redis()->exists($user_room_callTimes)){
            $times = redis()->get($user_room_callTimes);
        }else{
            $times = 1;
        }
        return (int)$times;
    }

    /**
     * 翻倍积分倍率
     * @param $room_no
     */
    protected function doubleRate($room_no){
        $game_conf = $this->game_conf;
        $room_data_key  = sprintf($game_conf['user_room_data'],$room_no);
        $room_data      = redis()->hGetAll($room_data_key);
        $user_info_data = json_decode($room_data['uinfo'],true);
        if(!isset($room_data['master'])){
            foreach ($user_info_data as $k=>$v){
                $user_info_data[$k]['rate'] = ($user_info_data[$k]['rate']*2);
            }
        }
        $update_data['uinfo'] = json_encode($user_info_data);
        $this->updateRoomData($room_data['room_no'],$update_data);
    }

    /**
     * 更新房间用户信息
     * @param $room_no
     * @param $account
     * @param $param
     */
    protected function setRoomUserData($room_no,$account,$param){
        $game_conf = config('game');
        $room_data_key = sprintf($game_conf['user_room_data'],$room_no);
        $room_user_data = redis()->hGetAll($room_data_key);
        $user_data = json_decode($room_user_data[$account],true);
        foreach ($param as $k=>$v){
            $user_data[$k] = $v;
        }
        $data[$account] = json_encode($user_data);
        redis()->hMSet($room_data_key,$data);
    }

    /**
     * 广播房间叫地主数据
     * @param $fds
     * @param $chair_id
     * @param $rate
     * @param $call_catch
     * @param $val
     */
    protected function broadcastRoom($fds,$chair_id,$rate,$call_catch, $val)
    {
        $msg = array('chair_id'=>$chair_id,'rate'=>$rate,'call_or_catch'=>$call_catch,'call_catch_val'=>$val);
        $data = Packet::packFormat('OK', 0, $msg);
        $data = Packet::packEncode($data, MainCmd::CMD_SYS, SubCmd::SUB_GAME_CALL_RESP);
        foreach($fds as $fd) {
            //获取客户端信息
            $client_info = $this->_params['serv']->getClientInfo($fd);
            $client[$fd] = $client_info;
            if(isset($client_info['websocket_status']) && $client_info['websocket_status'] == 3) {
                $this->_params['serv']->push($fd, $data, WEBSOCKET_OPCODE_BINARY);
            }
        }
    }

    /**
     * 查找下一家chair_id
     * @param $call_arr
     * @param $account
     * @return mixed
     */
    protected function findNextAccount($call_arr,$account){
        $curr_no = array_search($account,$call_arr);
        if(($curr_no+1)>2){
            $next_user_account = $call_arr[0];
        }else{
            $next_user_account = $call_arr[$curr_no+1];
        }
        return $next_user_account;
    }

    /**
     * @param $fds
     * @param $room_no
     */
    protected function catchGameCardResp($fds,$room_no)
    {
        $game_conf = $this->game_conf;
        $room_data_key = sprintf($game_conf['user_room_data'],$room_no);
        $room_data = redis()->hGetAll($room_data_key);
        $call_order = json_decode($room_data['call_order'],true);
        $num1 = json_decode($room_data[$call_order[0]],true);
        $num2 = json_decode($room_data[$call_order[1]],true);
        $num3 = json_decode($room_data[$call_order[2]],true);
        $call_times = $room_data['call_times'];
        $user_room_chair_key    = sprintf($game_conf['user_room_chair'],$room_no);
        $chair_users = json_decode(redis()->get($user_room_chair_key),true);//房间用户座位号
        // 如果没产生地主
        if(!isset($room_data['master'])){
            $caller_times_num = $this->getCallerTimes($room_no);
            //如果第三次玩家点击了
            if($call_times == 3){
                if(($num1['call_catch'] == 1) && ($num1['val'] == 0)){
                    if(($num2['call_catch'] == 1) && ($num2['val'] == 0) && ($num3['call_catch'] == 1) && ($num3['val'] == 0)){
                        // 清除准备状态
                        $this->cleanRoomPrepare($room_no);
                        // 清除房间数据
                        $this->cleanRoomData($room_no);
                        //清除计时器
                        $this->cleanCallLoop($room_no);
                        //投递异步发牌任务
                        $task = container()->get(GameSyncTask::class);
                        $task->assignCards($room_no);
                    }
                    if(($num2['call_catch'] == 1) && ($num2['val'] == 0) && ($num3['call_catch'] == 1) && ($num3['val'] == 1)){
                        $this->masterData($room_data['room_no'],$call_order[2]);
                    }
                    if(($num2['call_catch'] == 1) && ($num2['val'] == 1) && ($num3['call_catch'] == 2) && ($num3['val'] == 0)){
                        $this->masterData($room_data['room_no'],$call_order[1]);
                    }
                    if(($num2['call_catch'] == 1) && ($num2['val'] == 1) && ($num3['call_catch'] == 2) && ($num3['val'] == 1)){
                        //给第二家再次下发抢
                        $call_catch = 2;
                        $task = container()->get(CallNextTask::class);
                        $task->callNext($room_data['room_no'],$chair_users[$call_order[1]],$call_catch,4,$caller_times_num+1);
                    }
                }
                if(($num1['call_catch'] == 1) && ($num1['val'] == 1)){
                    if(($num2['call_catch'] == 2) && ($num2['val'] == 1)){
                        //给第一家再次下发抢
                        $call_catch = 2;
                        $task = container()->get(CallNextTask::class);
                        $task->callNext($room_data['room_no'],$chair_users[$call_order[0]],$call_catch,4,$caller_times_num+1);

                    }
                    if(($num2['call_catch'] == 2) && ($num2['val'] == 0) && ($num3['call_catch'] == 2) && ($num3['val'] == 1)){
                        //给第一家再次下发抢
                        $call_catch = 2;
                        $task = container()->get(CallNextTask::class);
                        $task->callNext($room_data['room_no'],$chair_users[$call_order[0]],$call_catch,4,$caller_times_num+1);
                    }
                    if(($num2['call_catch'] == 2) && ($num2['val'] == 0) && ($num3['call_catch'] == 2) && ($num3['val'] == 0)){
                        // 第一家地主
                        $this->masterData($room_data['room_no'],$call_order[0]);
                    }
                }
            }
            if($call_times == 4){
                if(($num1['call_catch'] == 2) && ($num1['val'] == 1)){
                    // 第一家地主
                    $this->masterData($room_data['room_no'],$call_order[0]);
                }
                if(($num1['call_catch'] == 1) && ($num1['val'] == 0) && ($num2['call_catch'] == 2) && ($num2['val'] == 1)){
                    // 第二家地主
                    $this->masterData($room_data['room_no'],$call_order[1]);
                }
                if(($num1['call_catch'] == 1) && ($num1['val'] == 0) && ($num2['call_catch'] == 2) && ($num2['val'] == 0)){
                    // 第三家地主
                    $this->masterData($room_data['room_no'],$call_order[2]);
                }
                if(($num1['call_catch'] == 2) && ($num1['val'] == 0) && ($num2['call_catch'] == 2) && ($num2['val'] == 0) && ($num3['call_catch'] == 2) && ($num3['val'] == 1)){
                    // 第三家地主
                    $this->masterData($room_data['room_no'],$call_order[2]);
                }
                if(($num1['call_catch'] == 2) && ($num1['val'] == 0) && ($num2['call_catch'] == 2) && ($num2['val'] == 1)){
                    // 第二家地主
                    $this->masterData($room_data['room_no'],$call_order[1]);
                }
            }
        }
    }

    /**
     * 地主数据
     * @param $room_no
     * @param $account
     */
    protected function masterData($room_no,$account){
        //抓牌,合并手牌数据
        $room_data_key = sprintf($this->game_conf['user_room_data'],$room_no);
        $room_data = redis()->hGetAll($room_data_key);
        $user_data = json_decode($room_data[$account], true);
        $hand = json_decode($room_data['hand'], true);
        $card = array_values(array_merge($user_data['card'], $hand));
        $card = $this->obj_ddz->_sortCardByGrade($card);
        $user_data['card'] = $card;
        //设置地主和用户手牌数据
        $param = array(
            'master'=>$account,
            $account=>json_encode($user_data)
        );
        $this->updateRoomData($room_no,$param);
        $this->catchGameCard($room_data, $account);
    }

    /**
     * 抓牌返回数据
     * @param $room_data
     * @param $user
     */
    protected function catchGameCard($room_data, $user)
    {
        $fds = $this->getRoomFds($user);
        $info = json_decode($room_data[$user], true);
        $msg = array(
            'account'=>$user,
            'chair_id' => $info['chair_id'],
            'hand_card' => json_decode($room_data['hand'],true)
        );
        $data = Packet::packFormat('OK', 1, $msg);
        $data = Packet::packEncode($data, MainCmd::CMD_SYS, SubCmd::SUB_GAME_CATCH_BASECARD_RESP);
        $this->pushToUsers($this->_params['serv'],$fds,$data);// 广播地主信息

        //清除计时器
        $this->cleanCallLoop($room_data['room_no']);

        // 清除叫牌计时器
        $this->cleanCallTimes($room_data['room_no']);

        //地主出牌广播
        $out_msg = array('next_chair_id'=>$info['chair_id'],'wait_time'=>$this->user_conf['play_timer']);
        $out_card = Packet::packFormat('OK', 1, $out_msg);
        $out_card = Packet::packEncode($out_card, MainCmd::CMD_SYS, SubCmd::SUB_GAME_OUT_CARD_LOOP_RESP);
        $this->pushToUsers($this->_params['serv'],$fds,$out_card);// 广播出牌信息

        usleep($this->user_conf['send_play_timer']);

        // 推下家出牌广播
        $param = [
            'next_chair_id'=>$info['chair_id'],   //下一个出牌的椅子id
            'wait_time'=>$this->user_conf['play_timer']
        ];
        $data_next = Packet::packFormat('OK', 1, $param);
        $data_next = Packet::packEncode($data_next, MainCmd::CMD_SYS, SubCmd::SUB_GAME_OUT_CARD_LOOP_RESP);
        $this->pushToUsers($this->_params['serv'],$fds, $data_next);

        // 投递出牌计时任务
        $task = container()->get(OutCardTimerTask::class);
        $task->outCardTimer($room_data['room_no'],$info['chair_id'],1);

    }

    /**
     * 获取重新发牌次数
     * @param $room_no
     * @return bool|mixed|string
     */
    protected function getAssignTimes($room_no){
        $game_conf = $this->game_conf;
        $room_assign_key = sprintf($game_conf['user_room_assign'],$room_no);
        if(redis()->exists($room_assign_key)){
            $times = redis()->get($room_assign_key);
        }else{
            $times = 1;
        }
        return $times;
    }

    /**
     * 设置重新发牌次数
     * @param $room_no
     * @param $times
     */
    protected function setAssignTimes($room_no,$times){
        $game_conf = $this->game_conf;
        $room_assign_key = sprintf($game_conf['user_room_assign'],$room_no);
        $new_times = $times+1;
        redis()->set($room_assign_key,$new_times,86400);
    }

    /**
     * 清除重新发牌次数计数
     * @param $room_no
     */
    protected function cleanAssignTimes($room_no){
        $game_conf = $this->game_conf;
        $room_assign_key = sprintf($game_conf['user_room_assign'],$room_no);
        redis()->del($room_assign_key);
    }

    /**
     * 清除房间号
     * @param $room_no
     */
    protected function cleanRoomNo($room_no){
        $game_conf = config('game');
        $user_room_total_key    = $game_conf['user_room_total'];
        redis()->sRem($user_room_total_key,$room_no);
    }
}